/* Copyright (c) 2019 285424336@qq.com.
 * All rights reserved.
 * Author: 285424336
 * License: LGPLV3
 */
#include <glib.h>
#include <glib/gstdio.h>

#include <cstdlib>
#include <memory>

#ifdef G_OS_WIN32
#ifdef USE_VLD
#include <vld.h>
#endif
#endif

const gchar* keyfile_path = "keyfile.ini";
const gchar* first_group_name = "first_group_name";
const gchar* first_key_name = "string";
const gchar* second_group_name = "second_group_name";
const gchar* second_key_name = "string_list";

void traverse_keyfile();
void delete_keyfile();
void create_keyfile();
void add_keyfile_item();
void contains_keyfile_item();
void delete_keyfile_item();

/**
 * @brief main 程序入口函数
 * @param argc 启动参数个数
 * @param argv 启动参数数组
 * @return 程序返回值
 */
int main(int argc, char** argv) {
    (void)argc;
    (void)argv;
    traverse_keyfile();
    delete_keyfile();
    create_keyfile();
    add_keyfile_item();
    contains_keyfile_item();
    delete_keyfile_item();
    return EXIT_SUCCESS;
}

void traverse_keyfile() {
    gchar** groups = nullptr;
    do {
        if (!g_file_test(keyfile_path, G_FILE_TEST_IS_REGULAR)) {
            break;
        }
        // 创建一个空的GKeyFile对象。
        // 使用g_key_file_load_from_file(),g_key_file_load_from_data(),g_key_file_load_from_dirs()或者g_key_file_load_from_data_dirs()来读取一个存在键值文件。
        std::shared_ptr<GKeyFile>  keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
            if (nullptr != keyfile) {
                // GKeyFile引用计数减1。
                // 如果引用计数为零，释放键值文件和所有其关联的内存。
                g_key_file_unref(keyfile);
            }
        });
        GError* error = nullptr;
        // 加载键值文件给空的GKeyFile结构。
        // 当打开或者读取文件的时候，如果操作系统返回错误，G_FILE_ERROR将被返回。
        // 如果解析文件有问题，G_KEY_FILE_ERROR将被返回。
        // 这函数将永远不会返回G_KEY_FILE_ERROR_NOT_FOUND错误码。
        // 如果文件没找到，G_FILE_ERROR_NOENT将被返回。
        gboolean boolean = g_key_file_load_from_file(keyfile.get(),
                                                     keyfile_path,
                                                     G_KEY_FILE_NONE,
                                                     &error);
        if (!boolean) {
            // 基于传入的err_no获取GFileError常亮。
            // 例如，如果你传入EEXIST函数将返回G_FILE_ERROR_EXIST。
            // 与error不同，你可以假定所有GFileError都将存在。
            // 通常GFileError值从一个操作文件的函数返回GError获取。
            // 所以当你构建一个GError时，你将使用g_file_error_from_errno()。
            GFileError file_error = g_file_error_from_errno(errno);
            if (G_FILE_ERROR == file_error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "G_FILE_ERROR == file_error\n");
            } else if (G_KEY_FILE_ERROR == file_error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "G_KEY_FILE_ERROR == file_error\n");
            } else {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "file_error = %d\n", file_error);
            }
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        // 返回文件开始组的名字
        gchar* start_group = g_key_file_get_start_group(keyfile.get());
        if (nullptr == start_group) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr == start_group\n");
            break;
        }
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "start_group = %s\n", start_group);
        g_free(start_group);
        start_group = nullptr;
        gsize groups_length = 0;
        // 返回在GKeyFile加载的键值文件中所有组。
        // 返回组的数组将以nullptr结尾，所以length可选择为nullptr。
        groups = g_key_file_get_groups(keyfile.get(),
                                       &groups_length);
        if (nullptr == groups) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr == groups\n");
            break;
        }
        for (gsize groups_index = 0; groups_index < groups_length; ++groups_index) {
            gsize keys_length = 0;
            // 返回组名字group_name中的所有键名字。
            // 返回键数组将以nullptr结尾，因此length可选择为nullptr。
            // 当group_name没有找到的时，返回nullptr并且error设置为G_KEY_FILE_ERROR_GROUP_NOT_FOUND。
            gchar** keys = g_key_file_get_keys(keyfile.get(),
                                               groups[groups_index],
                                               &keys_length,
                                               &error);
            if (nullptr == keys) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr == keys\n");
                break;
            }
            for (gsize keys_index = 0; keys_index < keys_length; ++keys_index) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "group %d = %s, key %d = %s\n", groups_index, groups[groups_index], keys_index, keys[keys_index]);
            }
            // 释放以nullptr结尾的strings数组，以及释放包含每一项。
            // 如果str_array为nullptr，该函数简单返回。
            g_strfreev(keys);
            keys = nullptr;
        }
    } while (0);
    if (nullptr != groups) {
        g_strfreev(groups);
        groups = nullptr;
    }
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "------------------------\n");
}

void delete_keyfile() {
    gboolean boolean = g_file_test(keyfile_path, G_FILE_TEST_EXISTS);
    do {
        if (!boolean) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "!boolean\n");
            break;
        }
        // POSIX函数remove()的封装。
        // remove()函数从文件系统删除名为name的项。
        // 关于remove()在你的系统中怎么工作的，参阅你的C库手册获取更多细节。
        // 在Unix中，remove()移除也包括目录，它为文件调用unlink()，为目录调用rmdir()。
        // 在Windows，尽管remove()在C库中仅工作于文件，该函数首先尝试remove()，如果失败则调用rmdir(),因此适用于文件和目录。
        // 但请注意，在Windows上，通常不可能移除一些进程打开或者映射到内存的文件。
        // 如果该函数在Windows调用失败，则你不能从errno推断出太多信息。
        // 无论什么原因导致的remove()失败，rmdir()都会被尝试调用。
        // remove()设置的任何errno值都将被rmdir()重写覆盖。
        int ret = g_remove(keyfile_path);
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "ret = %d\n", ret);
    } while (0);
}

void create_keyfile() {
    do {
        std::shared_ptr<GKeyFile>  keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
            if (nullptr != keyfile) {
                g_key_file_unref(keyfile);
            }
        });
        g_assert_nonnull(keyfile.get());
        // 在group_name下关联新的string给key。
        // 如果key没有找到则将被创建。
        // 如果group_name没有找到则被创建。
        // 不像g_key_file_set_value()，该函数处理需要转移的字符，例如换行。
        g_key_file_set_string(keyfile.get(),
                              first_group_name,
                              first_key_name,
                              "glib0");
        GError* error = nullptr;
        // 使用g_file_set_contents()写key_file内容到filename中。
        // 由于g_file_set_contents()可能失败的任何原因，该函数调可能失败。
        gboolean boolean = g_key_file_save_to_file(keyfile.get(),
                                                   keyfile_path,
                                                   &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
    } while (0);
    traverse_keyfile();
}

void add_keyfile_item() {
    do {
        std::shared_ptr<GKeyFile>  keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
            if (nullptr != keyfile) {
                g_key_file_unref(keyfile);
            }
        });
        g_assert_nonnull(keyfile.get());
        GError* error = nullptr;
        gboolean boolean = g_key_file_load_from_file(keyfile.get(),
                                                     keyfile_path,
                                                     G_KEY_FILE_NONE,
                                                     &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        const gchar* const list[3] = {{"glib0"},
                                      {"glib1"},
                                      {"glib2"}};
        // 关联key和string链表与group_name下。
        // 如果key没有找到则创建。
        // 如果group_name没有找到则创建。
        g_key_file_set_string_list(keyfile.get(),
                                   second_group_name,
                                   second_key_name,
                                   list,
                                   sizeof(list) / sizeof(list[0]));
        boolean = g_key_file_save_to_file(keyfile.get(),
                                          keyfile_path,
                                          &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
    } while (0);
    traverse_keyfile();
}

void contains_keyfile_item() {
    do {
        std::shared_ptr<GKeyFile>  keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
            if (nullptr != keyfile) {
                g_key_file_unref(keyfile);
            }
        });
        g_assert_nonnull(keyfile.get());
        GError* error = nullptr;
        gboolean boolean = FALSE;
        boolean = g_key_file_load_from_file(keyfile.get(),
                                            keyfile_path,
                                            G_KEY_FILE_NONE,
                                            &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        // 查看键值文件是否包含指定group_name
        boolean = g_key_file_has_group(keyfile.get(), first_group_name);
        if (!boolean) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "not contain groups %s\n", first_group_name);
            break;
        }
        // 返回group_name下和key关联的string。
        // 不像g_key_file_get_value()，该函数处理像\s这样的转义序列。
        // 如果key没有找到，nullptr将被返回，并且error设置为G_KEY_FILE_ERROR_KEY_NOT_FOUND。
        // 如果group_name没有找到，nullptr将被返回，并且error设置为G_KEY_FILE_ERROR_GROUP_NOT_FOUND.。
        gchar* string = g_key_file_get_string(keyfile.get(),
                                              first_group_name,
                                              first_key_name,
                                              &error);
        if (nullptr == string) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr == string\n");
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "string = %s\n", string);
        g_free(string);
        string = nullptr;
        // 查看key_file里面group_name是否包含key。
        // 请注意，此函数并不严格遵守GError规则；返回值既带有意义，又表示错误。
        // 使用该函数，你必须传递一个GError指针于error，并检查是否为非nullptr来看是否发生了错误。
        // 语言绑定应该使用g_key_file_get_value来测试key是否存在。
        boolean = g_key_file_has_key(keyfile.get(),
                                     second_group_name,
                                     second_key_name,
                                     &error);
        if (!boolean) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "not contain key %s\n", "string");
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        gsize length = 0;
        // 返回group_name下key关联的值。
        // 如果key没有找到，将返回nullptr并且error设置为G_KEY_FILE_ERROR_KEY_NOT_FOUND。
        // 如果group_name没有找到，将返回nullptr并且error设置为G_KEY_FILE_ERROR_GROUP_NOT_FOUND。
        gchar** string_list = g_key_file_get_string_list(keyfile.get(),
                                                         second_group_name,
                                                         second_key_name,
                                                         &length,
                                                         &error);
        if (nullptr == string_list) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr == string_list\n");
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        for (gsize string_list_index = 0; string_list_index < length; ++string_list_index) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "string_list_index(%d) = %s\n", string_list_index, string_list[string_list_index]);
        }
        g_strfreev(string_list);
        string_list = nullptr;
    } while (0);
    traverse_keyfile();
}

void delete_keyfile_item() {
    do {
        std::shared_ptr<GKeyFile>  keyfile(g_key_file_new(), [](GKeyFile* keyfile) {
            if (nullptr != keyfile) {
                g_key_file_unref(keyfile);
            }
        });
        g_assert_nonnull(keyfile.get());
        GError* error = nullptr;
        gboolean boolean = g_key_file_load_from_file(keyfile.get(),
                                                     keyfile_path,
                                                     G_KEY_FILE_NONE,
                                                     &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
        // 从key_file从移除指定group_name的组。
        boolean = g_key_file_remove_group(keyfile.get(),
                                          second_group_name,
                                          &error);
        boolean = g_key_file_save_to_file(keyfile.get(),
                                          keyfile_path,
                                          &error);
        if (!boolean) {
            if (nullptr != error) {
                g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
                g_error_free(error);
                error = nullptr;
            }
            break;
        }
    } while (0);
    traverse_keyfile();
}
